共计 10930 个字符,预计需要花费 28 分钟才能阅读完成。
为什么用 rime
在当今数字时代,大数据分析、广告联盟和精准营销已成为常态。然而,这些看似便利的技术背后,往往伴随着个人隐私的深度挖掘与潜在泄露。在众多可能导致隐私泄露的途径中,我们日常频繁使用的输入法无疑是其中一个重灾区。
以我个人为例,曾长期使用搜狗输入法。尽管我凭借一定的电脑使用经验,努力屏蔽了其大部分冗余功能和广告弹窗,但近期搜狗爆出的“云控下发模块”丑闻(火绒分析搜狗云控下发模块)彻底打破了这种“安全感”。这并非孤例。日常生活中,我们屡次发现,在输入某些关键词后,很快便能收到高度相关的精准推销广告;输入法读取剪切板的行为更是屡见不鲜。这些现象无不指向一个事实:我们的打字内容、复制粘贴信息等敏感数据,正被输入法厂商无底线地收集、分析乃至滥用。
诚然,云计算技术为输入法带来了便捷的云同步、词库更新等功能,但其背后却是用户数据的大量上传与潜在泄露。这种以牺牲隐私换取便利的做法,已让我无法接受。
在当前数字环境下,完全杜绝隐私泄露或许难以实现。然而,“往者不可谏,来者犹可追”,我们并非束手无策。面对厂商的无底线行为,我们至少可以选择主动出击,尽可能减少个人数据泄露的途径,重新审视并选择更注重隐私保护的工具。
在对主流输入法厂商的无底线行为感到失望之后,许多重视隐私的用户将目光投向了开源且高度可定制的 Rime输入法框架。Rime,在不同操作系统下有不同的前端名称,例如在Windows上常被称为“小狼毫”,在macOS上是“鼠鬚管”,而在Linux上则被称为“中州韵”。
Rime与我们之前讨论的那些主流输入法有着本质的区别:它的核心理念是“本地化”和“用户主导”。这意味着,Rime在设计之初就将用户隐私放在首位。它不上传任何用户数据到云端,没有所谓的“云同步”功能(除非用户自行配置第三方服务),更不会有广告推送或数据分析行为。你的输入习惯、词库、个人配置,都完全存储在本地设备上,数据的控制权牢牢掌握在你手中。这彻底避免了因云端数据泄露或厂商滥用数据而带来的隐私风险。
Rime的另一个显著特点是其极高的可定制性。它提供了一个高度灵活的配置系统,允许用户从输入方案、词库、按键布局到界面主题,进行几乎无限制的个性化设置。这与那些功能臃肿、广告缠身、且用户无权干预的商业输入法形成了鲜明对比,真正实现了“我的输入法我做主”。
当然,这种极致的自由和隐私保护并非没有代价。Rime的上手门槛相对较高,初次配置可能需要投入一定的时间和精力去学习其配置文件和逻辑。但对于那些真正重视数据安全、厌倦了被厂商“喂养”的用户而言,这无疑是一笔值得的投资。它不仅仅是一个输入法,更是一种对个人数据主权和数字自由的坚守。
我日常使用的平台是:Windows + Android,下文会基于此介绍我的配置方案。
Windows:小狼毫
Android:雨燕输入法
Windows配置
下载安装
下载链接:小狼毫官网
安装过程不赘述,一路点击即可。
用户数据路径为:%APPDATA%\Rime
主题配置
个人习惯 微软输入法 的主题风格,修改 用户数据路径 下的 weasel.custom.yaml
:
patch:
preset_color_schemes:
microsoft_sim:
name: 仿微软输入法
author: plutotree
back_color: 0xF4F4F4
border_color: 0xDCDCDC
text_color: 0x000000
hilited_text_color: 0xF4F4F4
hilited_back_color: 0xFFD8A6
hilited_candidate_text_color: 0x000000
hilited_candidate_back_color: 0xFFD8A6
candidate_text_color: 0x000000
comment_text_color: 0x888888
style:
color_scheme: microsoft_sim
label_format: "%s"
font_face: "微软雅黑"
font_point: 13
horizontal: false
inline_preedit: true
layout:
min_width: 140
min_height: 0
border_width: 1
border_height: 1
margin_x: 9
margin_y: 9
spacing: 5
candidate_spacing: 0
hilite_spacing: 7
hilite_padding: 2
round_corner: 0
第三方词库配置
默认词库并不好用,选用白霜词库:白霜拼音
下载后,将文件复制到用户数据路径:%APPDATA%\Rime
在输入法设定
中,启用白霜词库,选择访微软风格皮肤:
个人历史词库导入
从搜狗输入法PC端导出用户词库bin文件:
下载深蓝词库转换,将搜狗bin词库,转换为 rime中州韵 词库txt文件:
在小狼毫用户词典管理导入txt:
PC端同步
需要自行架设webdav服务,可参考站内文章:webdav 部署和使用。
修改设备ID和同步路径,修改 用户数据路径 下的:installation.yaml
# 增加两行
installation_id: "HD-WORK-WIN-1"
sync_dir: "X:/rime-sync"
手动同步,同步完后点击重新部署:
自动同步可通过编写同步脚本和添加任务计划程序,可参考:我的rime:打造多端同步的本地输入法。
移动端
移动端下有3个基于 rime框架 的输入法,可参考:手机输入法使用体验。
我选择了雨燕输入法,因为更简单,而且符合我之前的使用习惯。
安卓导入个人历史词库
雨燕输入法 没办法实现实时同步,因为它不直接兼容用户导入rime用户数据,有自己的导入导出解决方案。
我只期望将历史词库导入即可。
写了1个 python 脚本,用于合并存量个人词库。
先在安卓设备雨燕输入法上导出用户数据ZIP包:
windows10设备上已经导入好了词库,完整打包 用户数据路径下的 rime_frost.userdb
目录:
需要注意复制前要彻底关闭小狼毫,否则文件被占用无法执行读取操作。
最简单的方式是卸载程序。卸载不会对用户数据路径做变更,待复制完毕后再重新安装即可。
将 雨燕输入法的ZIP包 和 小狼毫的数据目录 完整放置到Ubuntu服务器上,添加如下脚本并执行(路径自行修改):
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Rime词库合并工具 - 标准版
将小狼毫词库合并到雨燕词库中,保留雨燕原有数据和元数据
"""
import plyvel
import zipfile
import os
import shutil
import time
import sys
# 配置变量
YUYAN_ORIGINAL_ZIP = "/ops/rime/yuyanIme_2025-09-25_09_05_00.zip"
XIAOLANGHAO_DB_PATH = "/ops/rime/rime_frost.userdb"
OUTPUT_ZIP = "/ops/rime/yuyan_merged_final.zip"
TEMP_DIR = "/ops/rime/temp_merge_final"
VERIFY_TEMP_DIR = "/ops/rime/temp_verify_final"
def print_step(step_num, total_steps, description):
"""标准化步骤输出"""
print(f"[{step_num}/{total_steps}] {description}")
def print_info(message, indent=0):
"""标准化信息输出"""
prefix = " " * indent
print(f"{prefix}INFO: {message}")
def print_stats(title, stats_dict, indent=0):
"""标准化统计信息输出"""
prefix = " " * indent
print(f"{prefix}{title}:")
for key, value in stats_dict.items():
print(f"{prefix} - {key}: {value}")
def analyze_database(db_path, db_name):
"""分析数据库内容并返回统计信息"""
print_info(f"正在分析{db_name}数据库: {db_path}")
try:
db = plyvel.DB(db_path, create_if_missing=False)
metadata = {}
words = {}
for key, value in db:
if key.startswith(b'\x01/'):
metadata[key] = value
else:
words[key] = value
db.close()
# 计算数据库文件大小
total_size = 0
if os.path.exists(db_path):
for item in os.listdir(db_path):
item_path = os.path.join(db_path, item)
if os.path.isfile(item_path):
total_size += os.path.getsize(item_path)
stats = {
"元数据条目": len(metadata),
"用户词条": len(words),
"总记录数": len(metadata) + len(words),
"数据库大小": f"{total_size / 1024:.1f} KB"
}
print_stats(f"{db_name}统计信息", stats, indent=1)
return {
"metadata": metadata,
"words": words,
"stats": stats,
"total_size": total_size
}
except Exception as e:
print(f"ERROR: 无法分析{db_name}数据库: {e}")
return None
def extract_yuyan_zip():
"""解压雨燕ZIP包并分析内容"""
print_info(f"解压雨燕ZIP包: {YUYAN_ORIGINAL_ZIP}")
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
try:
with zipfile.ZipFile(YUYAN_ORIGINAL_ZIP, 'r') as zipf:
file_list = zipf.namelist()
zipf.extractall(TEMP_DIR)
print_info(f"ZIP包含文件数: {len(file_list)}")
# 分析解压后的雨燕数据库
yuyan_db_path = os.path.join(TEMP_DIR, "external/rime/pinyin.userdb")
yuyan_data = analyze_database(yuyan_db_path, "雨燕")
return file_list, yuyan_data
except Exception as e:
print(f"ERROR: 解压雨燕ZIP包失败: {e}")
return None, None
def merge_databases(yuyan_data, xiaolanghao_data):
"""合并数据库内容"""
print_info("开始合并数据库内容")
# 从雨燕数据开始(包含元数据和词条)
merged_data = yuyan_data["metadata"].copy()
merged_data.update(yuyan_data["words"])
# 统计信息
original_word_count = len(yuyan_data["words"])
xiaolanghao_word_count = len(xiaolanghao_data["words"])
# 添加小狼毫词条(只添加雨燕没有的)
added_count = 0
duplicate_count = 0
for key, value in xiaolanghao_data["words"].items():
if key in merged_data:
duplicate_count += 1
else:
merged_data[key] = value
added_count += 1
final_word_count = len([k for k in merged_data.keys() if not k.startswith(b'\x01/')])
final_metadata_count = len([k for k in merged_data.keys() if k.startswith(b'\x01/')])
merge_stats = {
"雨燕原有词条": original_word_count,
"小狼毫总词条": xiaolanghao_word_count,
"重复词条": duplicate_count,
"新增词条": added_count,
"合并后词条总数": final_word_count,
"元数据条目": final_metadata_count,
"合并后总记录": len(merged_data)
}
print_stats("合并统计", merge_stats, indent=1)
return merged_data, merge_stats
def rebuild_database(merged_data):
"""重建数据库"""
print_info("重建数据库")
new_db_path = os.path.join(TEMP_DIR, "external/rime/pinyin.userdb")
# 删除旧数据库
if os.path.exists(new_db_path):
shutil.rmtree(new_db_path)
print_info("已删除旧数据库文件", indent=1)
try:
# 创建新数据库
new_db = plyvel.DB(new_db_path, create_if_missing=True)
# 写入所有数据
write_count = 0
for key, value in merged_data.items():
new_db.put(key, value)
write_count += 1
if write_count % 5000 == 0:
print_info(f"已写入 {write_count} 条记录", indent=1)
# 强制同步数据库
new_db.close()
time.sleep(2) # 等待文件系统同步
print_info(f"数据库重建完成,共写入 {write_count} 条记录", indent=1)
# 验证重建后的数据库
return verify_rebuilt_database(new_db_path, len(merged_data))
except Exception as e:
print(f"ERROR: 重建数据库失败: {e}")
return False
def verify_rebuilt_database(db_path, expected_count):
"""验证重建后的数据库"""
print_info("验证重建后的数据库", indent=1)
try:
# 检查文件大小
total_size = 0
db_files = []
for item in os.listdir(db_path):
item_path = os.path.join(db_path, item)
if os.path.isfile(item_path):
size = os.path.getsize(item_path)
total_size += size
db_files.append(f"{item}: {size} bytes")
print_info(f"数据库文件: {len(db_files)} 个", indent=2)
print_info(f"数据库总大小: {total_size / 1024:.1f} KB", indent=2)
if total_size < 100000: # 小于100KB可能有问题
print("WARNING: 数据库大小异常小")
return False
# 验证数据可读性
verify_db = plyvel.DB(db_path, create_if_missing=False)
actual_count = 0
for key, value in verify_db:
actual_count += 1
verify_db.close()
print_info(f"验证记录数: {actual_count} (期望: {expected_count})", indent=2)
if actual_count == expected_count:
print_info("数据库验证成功", indent=2)
return True
else:
print(f"ERROR: 记录数不匹配")
return False
except Exception as e:
print(f"ERROR: 数据库验证失败: {e}")
return False
def create_final_zip(file_list):
"""创建最终的ZIP包"""
print_info(f"创建最终ZIP包: {OUTPUT_ZIP}")
try:
with zipfile.ZipFile(OUTPUT_ZIP, 'w', zipfile.ZIP_DEFLATED, compresslevel=6) as new_zip:
for file_path in file_list:
full_path = os.path.join(TEMP_DIR, file_path)
if file_path == 'external/rime/pinyin.userdb/':
# 特殊处理数据库目录
db_dir = os.path.join(TEMP_DIR, 'external/rime/pinyin.userdb')
new_zip.writestr('external/rime/pinyin.userdb/', '')
db_file_count = 0
for db_file in sorted(os.listdir(db_dir)):
db_file_path = os.path.join(db_dir, db_file)
if os.path.isfile(db_file_path):
arc_path = f'external/rime/pinyin.userdb/{db_file}'
new_zip.write(db_file_path, arc_path)
db_file_count += 1
print_info(f"已添加数据库文件: {db_file_count} 个", indent=1)
elif os.path.exists(full_path) and not file_path.startswith('external/rime/pinyin.userdb/'):
new_zip.write(full_path, file_path)
zip_size = os.path.getsize(OUTPUT_ZIP)
print_info(f"ZIP包创建完成,大小: {zip_size / 1024:.1f} KB", indent=1)
return True
except Exception as e:
print(f"ERROR: 创建ZIP包失败: {e}")
return False
def final_verification():
"""最终验证"""
print_info("执行最终验证")
if os.path.exists(VERIFY_TEMP_DIR):
shutil.rmtree(VERIFY_TEMP_DIR)
try:
# 解压最终ZIP包
with zipfile.ZipFile(OUTPUT_ZIP, 'r') as zipf:
zipf.extractall(VERIFY_TEMP_DIR)
# 验证数据库
final_db_path = os.path.join(VERIFY_TEMP_DIR, "external/rime/pinyin.userdb")
final_data = analyze_database(final_db_path, "最终")
if final_data:
print_info("最终验证成功", indent=1)
return True
else:
print("ERROR: 最终验证失败")
return False
except Exception as e:
print(f"ERROR: 最终验证失败: {e}")
return False
finally:
if os.path.exists(VERIFY_TEMP_DIR):
shutil.rmtree(VERIFY_TEMP_DIR)
def cleanup():
"""清理临时文件"""
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
if os.path.exists(VERIFY_TEMP_DIR):
shutil.rmtree(VERIFY_TEMP_DIR)
def main():
"""主函数"""
print("Rime词库合并工具 - 标准版")
print("=" * 50)
total_steps = 7
try:
# 步骤1: 解压并分析雨燕ZIP包
print_step(1, total_steps, "解压并分析雨燕ZIP包")
file_list, yuyan_data = extract_yuyan_zip()
if not yuyan_data:
return False
# 步骤2: 分析小狼毫数据库
print_step(2, total_steps, "分析小狼毫数据库")
xiaolanghao_data = analyze_database(XIAOLANGHAO_DB_PATH, "小狼毫")
if not xiaolanghao_data:
return False
# 步骤3: 合并数据库内容
print_step(3, total_steps, "合并数据库内容")
merged_data, merge_stats = merge_databases(yuyan_data, xiaolanghao_data)
# 步骤4: 重建数据库
print_step(4, total_steps, "重建数据库")
if not rebuild_database(merged_data):
return False
# 步骤5: 创建最终ZIP包
print_step(5, total_steps, "创建最终ZIP包")
if not create_final_zip(file_list):
return False
# 步骤6: 最终验证
print_step(6, total_steps, "最终验证")
if not final_verification():
return False
# 步骤7: 清理临时文件
print_step(7, total_steps, "清理临时文件")
cleanup()
print("=" * 50)
print("合并完成!")
print(f"输出文件: {OUTPUT_ZIP}")
print("请将此文件导入到雨燕输入法")
return True
except Exception as e:
print(f"ERROR: 合并过程失败: {e}")
cleanup()
return False
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)
执行后效果如下,将输出的包导入到雨燕输入法即可:
[root@ ubuntu-ops /ops/rime]
10:31:12 # python3 rime_yuyan_merge_xiaolanghao.py
Rime词库合并工具 - 标准版
==================================================
[1/7] 解压并分析雨燕ZIP包
INFO: 解压雨燕ZIP包: /ops/rime/yuyanIme_2025-09-25_09_05_00.zip
INFO: ZIP包含文件数: 66
INFO: 正在分析雨燕数据库: /ops/rime/temp_merge_final/external/rime/pinyin.userdb
雨燕统计信息:
- 元数据条目: 5
- 用户词条: 387
- 总记录数: 392
- 数据库大小: 17.9 KB
[2/7] 分析小狼毫数据库
INFO: 正在分析小狼毫数据库: /ops/rime/rime_frost.userdb
小狼毫统计信息:
- 元数据条目: 5
- 用户词条: 37171
- 总记录数: 37176
- 数据库大小: 1593.3 KB
[3/7] 合并数据库内容
INFO: 开始合并数据库内容
合并统计:
- 雨燕原有词条: 387
- 小狼毫总词条: 37171
- 重复词条: 128
- 新增词条: 37043
- 合并后词条总数: 37430
- 元数据条目: 5
- 合并后总记录: 37435
[4/7] 重建数据库
INFO: 重建数据库
INFO: 已删除旧数据库文件
INFO: 已写入 5000 条记录
INFO: 已写入 10000 条记录
INFO: 已写入 15000 条记录
INFO: 已写入 20000 条记录
INFO: 已写入 25000 条记录
INFO: 已写入 30000 条记录
INFO: 已写入 35000 条记录
INFO: 数据库重建完成,共写入 37435 条记录
INFO: 验证重建后的数据库
INFO: 数据库文件: 5 个
INFO: 数据库总大小: 2073.6 KB
INFO: 验证记录数: 37435 (期望: 37435)
INFO: 数据库验证成功
[5/7] 创建最终ZIP包
INFO: 创建最终ZIP包: /ops/rime/yuyan_merged_final.zip
INFO: 已添加数据库文件: 7 个
INFO: ZIP包创建完成,大小: 30499.6 KB
[6/7] 最终验证
INFO: 执行最终验证
INFO: 正在分析最终数据库: /ops/rime/temp_verify_final/external/rime/pinyin.userdb
最终统计信息:
- 元数据条目: 5
- 用户词条: 37430
- 总记录数: 37435
- 数据库大小: 687.1 KB
INFO: 最终验证成功
[7/7] 清理临时文件
==================================================
合并完成!
输出文件: /ops/rime/yuyan_merged_final.zip
请将此文件导入到雨燕输入法
总结
初次使用过程的确比较麻烦,特别是将历史词库导入到雨燕花费了我很大的精力。
跨设备的剪切板同步,我使用了 SyncClipboard,也是基于webdav实现。不过安卓设备要实现自动同步依赖获取 Root权限。